//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Windows;
using System.Windows.Controls;

namespace OpenLS.Chart.UI.Internals
{
    public class CategoryAxisPanel : Panel
    {
        public static readonly DependencyProperty AllowsOverlapProperty =
            DependencyProperty.Register("AllowsOverlap", typeof (bool), typeof (CategoryAxisPanel),
                                        new UIPropertyMetadata(false));

        public static readonly DependencyProperty AxisOrientationProperty =
            DependencyProperty.Register("AxisOrientation", typeof (AxisOrientation), typeof (CategoryAxisPanel),
                                        new FrameworkPropertyMetadata(AxisOrientation.MinimumToMaximum,
                                                                      FrameworkPropertyMetadataOptions.AffectsArrange));

        public static readonly DependencyProperty CategoryAxisOrientationProperty =
            DependencyProperty.RegisterAttached("CategoryAxisOrientation", typeof (AxisOrientation),
                                                typeof (FrameworkElement),
                                                new FrameworkPropertyMetadata(AxisOrientation.MinimumToMaximum,
                                                                              FrameworkPropertyMetadataOptions.
                                                                                  AffectsArrange));

        public static readonly DependencyProperty IndexProperty =
            DependencyProperty.RegisterAttached("Index", typeof (int), typeof (FrameworkElement),
                                                new UIPropertyMetadata(0));

        public static readonly DependencyProperty MaximumProperty =
            DependencyProperty.Register("Maximum", typeof (int), typeof (CategoryAxisPanel),
                                        new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsArrange));

        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register("Orientation", typeof (Orientation), typeof (CategoryAxisPanel),
                                        new FrameworkPropertyMetadata(Orientation.Horizontal,
                                                                      FrameworkPropertyMetadataOptions.AffectsArrange));

        public static readonly DependencyProperty UsesMarginsProperty =
            DependencyProperty.RegisterAttached("UsesMargins", typeof (bool), typeof (CategoryAxisPanel),
                                                new FrameworkPropertyMetadata(true,
                                                                              FrameworkPropertyMetadataOptions.
                                                                                  AffectsArrange));


        public bool AllowsOverlap
        {
            get { return (bool) GetValue(AllowsOverlapProperty); }
            set { SetValue(AllowsOverlapProperty, value); }
        }


        public double Gap
        {
            get { return (double)GetValue(GapProperty); }
            set { SetValue(GapProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Gap.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GapProperty =
            DependencyProperty.Register("Gap", typeof(double), typeof(CategoryAxisPanel), new UIPropertyMetadata(4.0));



        public AxisOrientation AxisOrientation
        {
            get { return (AxisOrientation) GetValue(AxisOrientationProperty); }
            set { SetValue(AxisOrientationProperty, value); }
        }

        public Orientation Orientation
        {
            get { return (Orientation) GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        public int Maximum
        {
            get { return (int) GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            int max = int.MinValue;
            int min = int.MaxValue;
            foreach (DependencyObject e in InternalChildren)
            {
                max = Math.Max(max, GetIndex(e));
                min = Math.Min(min, GetIndex(e));
            }
            min = 0;
            max = Maximum;
            int delta = max - min + 1;
            if (delta <= 0)
                return finalSize;
            AxisOrientation axisOrientation = GetCategoryAxisOrientation(this);
            bool usesMargins = GetUsesMargins(this);
            var previousRect = new Rect(new Point(-10000, -10000), new Size(0, 0));
            bool allowOverlap = AllowsOverlap;
            double gap = Gap;
            Orientation orientation = Orientation;
            foreach (FrameworkElement e in InternalChildren)
            {
                int index = GetIndex(e) - min;
                Rect rect = GetRect(orientation, index, min, max, finalSize, axisOrientation, usesMargins, e.DesiredSize);
                if (!allowOverlap && previousRect.IntersectsWith(rect))
                {
                    e.Arrange(new Rect());
                }
                else
                {
                    e.Arrange(rect);
                    previousRect = new Rect(rect.Location, new Size(rect.Width + gap, rect.Height + gap));

                }
            }
            return base.ArrangeOverride(finalSize);
        }

        private static Rect GetRect(Orientation orientation, int index, int min, int max,
                                    Size finalSize, AxisOrientation axisOrientation, bool usesMargins, Size desiredSize)
        {
            if (orientation == Orientation.Horizontal)
            {
                return new Rect(new Point(
                                    GetScreenPosition(index, min, max, 0, finalSize.Width, axisOrientation, usesMargins) -
                                    desiredSize.Width/2, 0),
                                new Size(desiredSize.Width, finalSize.Height));
            }

            return new Rect(new Point(
                                0,
                                GetScreenPosition(index, min, max, finalSize.Height, 0, axisOrientation,
                                                  usesMargins) - desiredSize.Height/2),
                            new Size(finalSize.Width, desiredSize.Height));
        }

        internal static double GetScreenPosition(double index, int minIndex, int maxIndex, double screenMin,
                                                 double screenMax, AxisOrientation orientation, bool usesMargins)
        {
            if (minIndex == maxIndex)
                return (screenMin + screenMax)/2;
            if (orientation == AxisOrientation.MaximumToMinimum)
                return GetScreenPosition(index, minIndex, maxIndex, screenMax, screenMin,
                                         AxisOrientation.MinimumToMaximum, usesMargins);
            double delta = maxIndex - minIndex;
            if (usesMargins)
                delta++;
            double leftMargin = 0;
            if (usesMargins)
                leftMargin = (screenMax - screenMin)/delta/2;
            //double itemWidth = (screenMax - screenMin) / delta;
            return screenMin + leftMargin + (screenMax - screenMin)*(index - minIndex)/delta;
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            Size result = PanelHelper.MeasureChildren(availableSize, InternalChildren,
                                                      Orientation == Orientation.Horizontal);
            result = new Size(Math.Min(result.Width, availableSize.Width), Math.Min(result.Height, availableSize.Height));
            return result;
        }


        public static bool GetUsesMargins(DependencyObject obj)
        {
            return (bool) obj.GetValue(UsesMarginsProperty);
        }

        public static void SetUsesMargins(DependencyObject obj, bool value)
        {
            obj.SetValue(UsesMarginsProperty, value);
        }

        // Using a DependencyProperty as the backing store for UsesMargirs.  This enables animation, styling, binding, etc...


        public static AxisOrientation GetCategoryAxisOrientation(DependencyObject obj)
        {
            return (AxisOrientation) obj.GetValue(CategoryAxisOrientationProperty);
        }

        public static void SetCategoryAxisOrientation(DependencyObject obj, AxisOrientation value)
        {
            obj.SetValue(CategoryAxisOrientationProperty, value);
        }

        // Using a DependencyProperty as the backing store for AxisOrientation.  This enables animation, styling, binding, etc...


        public static int GetIndex(DependencyObject obj)
        {
            return (int) obj.GetValue(IndexProperty);
        }

        public static void SetIndex(DependencyObject obj, int value)
        {
            obj.SetValue(IndexProperty, value);
        }

        // Using a DependencyProperty as the backing store for Index.  This enables animation, styling, binding, etc...
    }
}